/*************************************************************************
    > File Name    : KeyRecommander.cc
    > Author       : LiuLei
    > Mail         : 2261459486@qq.com 
    > Created Time : 2022年11月30日 星期三 10时40分08秒
 ************************************************************************/

#include "../../include/server/KeyRecommander.h"
#include "../../include/server/CandidateResult.h"
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <queue>
using std::vector;
using std::pair;
using std::map;
using std::set;
using std::cout;
using std::endl;
using std::priority_queue;

KeyRecommander::KeyRecommander(Dictionary *dict)
: _dict(dict)
{

}

KeyRecommander::~KeyRecommander()
{

}

string KeyRecommander::doQuery(const string &query)
{
    _queryWord = query;
    vector<pair<string, int>> &dict = _dict->getDictionary();
    map<string, set<int>> &index = _dict->getIndexTable();

    size_t charNums = getUtf8CharNum(_queryWord);
    cout << "charNums : " << charNums << endl;

    set<string> uniqueSubWords;     // 保存去重后的单个word
    map<string, int> uniqueWords;   // 保存去重后的候选词

    for(size_t idx = 0, n = 0; n != charNums; ++idx, ++n)
    {
        size_t charLen = getUtf8ByteNum(_queryWord[idx]);
        string subWord = _queryWord.substr(idx, charLen);

        cout << "charLen : " << charLen << endl;
        cout << "subWord : " << subWord << endl;

        uniqueSubWords.insert(subWord);

        idx += (charLen - 1);
    }
    cout << "uniqueSubWords' size : " << uniqueSubWords.size() << endl;

    for(auto &usw : uniqueSubWords)
    {
        auto indexIt = index.find(usw);
        if(indexIt != index.end())
        {
            for(auto &index: indexIt->second)
            {
                cout << "index No : " << index << endl;

                string word = dict[index].first;
                int frequency = dict[index].second;
                cout << "word : " << word << ", frequency : " << frequency << endl;

                uniqueWords.insert({word, frequency});
            }
        }
        else
        {
            continue;
        }
    }
    cout << "uniqueWords' size : " << uniqueWords.size() << endl;
    
    priority_queue<CandidateResult> resultQueue;
    // 插入优先级队列
    for(auto &uw : uniqueWords)
    {
        CandidateResult cr;
        cr._word = uw.first;
        cr._freq = uw.second;
        cr._dist = distance(cr._word);
        resultQueue.push(cr);
    }

    // 判断resultQueue的结果，暂定返回五个，不足五个全部返回
    string result;
    if(resultQueue.empty())
    {
        result = "#";
    }
    else
    {
        size_t number = resultQueue.size();
        cout << "resultQueue's size : " << number << endl;

        if(number < 5)
        {
            while(number--)
            {
                result += resultQueue.top()._word;
                result += "  ";
                resultQueue.pop();
            }
        }
        else
        {
            int cnt = 5;
            while(cnt--)
            {
                result += resultQueue.top()._word;
                result += "  ";
                resultQueue.pop();
            }
        }
    }

    return result;
}

int KeyRecommander::distance(const string &rhs)
{
    size_t lhs_len = this->getUtf8CharNum(this->_queryWord);
    size_t rhs_len = this->getUtf8CharNum(rhs);

	int editDist[lhs_len + 1][rhs_len + 1];
	for(size_t idx = 0; idx <= lhs_len; ++idx)
	{
		editDist[idx][0] = idx;
	}

	for(size_t idx = 0; idx <= rhs_len; ++idx)
	{
		editDist[0][idx] = idx;
	}
	
	string sublhs, subrhs;
	for(size_t dist_i = 1, lhs_idx = 0; dist_i <= lhs_len; ++dist_i, ++lhs_idx)
	{
		size_t nBytes = this->getUtf8ByteNum(this->_queryWord[lhs_idx]);
		sublhs = this->_queryWord.substr(lhs_idx, nBytes);
		lhs_idx += (nBytes - 1);

		for(size_t dist_j = 1, rhs_idx = 0; dist_j <= rhs_len; ++dist_j, ++rhs_idx)
		{
			nBytes = this->getUtf8ByteNum(rhs[rhs_idx]);
			subrhs = rhs.substr(rhs_idx, nBytes);
			rhs_idx += (nBytes - 1);
			if(sublhs == subrhs)
			{
				editDist[dist_i][dist_j] = editDist[dist_i - 1][dist_j - 1];
			}
			else
			{
				editDist[dist_i][dist_j] = triple_min(
					editDist[dist_i][dist_j - 1] + 1,
					editDist[dist_i - 1][dist_j] + 1,
					editDist[dist_i - 1][dist_j - 1] + 1);
			}
		}
	}
	return editDist[lhs_len][rhs_len];
}

int KeyRecommander::triple_min(const int &a, const int &b, const int &c)
{
    return a < b ? (a < c ? a : c) : (b < c ? b : c);
}

size_t KeyRecommander::getUtf8ByteNum(const char byte)
{
    size_t byteNum = 0;
    for(size_t i = 0; i < 6; ++i)
    {
        if(byte & (1 << (7 - i)))
        {
            ++byteNum;
        }
        else
        {
            break;
        }
    }
    return byteNum == 0 ? 1 : byteNum;
}

size_t KeyRecommander::getUtf8CharNum(const string &str)
{
    size_t CharNum = 0;
    for(size_t idx = 0; idx != str.size(); ++idx)
    {
        int nBytes = this->getUtf8ByteNum(str[idx]);
        idx += (nBytes - 1);
        ++CharNum;
    }
    return CharNum;
}
